using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.ServiceModel;
using System.Xml;
using OpenComposite.HumanActivity.Contracts;
using OpenComposite.HumanActivity.Data;
using System.Reflection;

namespace OpenComposite.HumanActivity.Services
{
	[ServiceBehavior(
		ConcurrencyMode = ConcurrencyMode.Multiple,
		InstanceContextMode = InstanceContextMode.Single,
		Namespace = "http://www.theopencomposite.com/HumanActivity")]
	public class HumanActivityFormService : IHumanActivityFormService
	{
		#region Initialization
		public HumanActivityFormService(DataRow row, EventLog eventLog)
		{
			_row = row;
			_eventLog = eventLog;
		}
		#endregion

		#region Private Fields
		private DataRow _row;
		private EventLog _eventLog;
		#endregion

		#region IHumanActivityFormService Members

		/// <summary>
		/// Starts the activity.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <returns></returns>
		public bool StartActivity(int activityId, int userId)
		{
			try {
				return Global.DataAccess.StartActivity(activityId, userId);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("StartActivity", ex, activityId, userId);
			}
		}
		/// <summary>
		/// Cancels the activity.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <returns></returns>
		public bool CancelActivity(int activityId, int userId)
		{
			try {
				return Global.DataAccess.CancelActivity(activityId, userId);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("StartActivity", ex, activityId, userId);
			}
		}
		/// <summary>
		/// Posts the pone activity.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <param name="dtPostPone">The dt post pone.</param>
		/// <returns></returns>
		public bool PostPoneActivity(int activityId, int userId, DateTime dtPostPone)
		{
			try {
				return Global.DataAccess.PostPoneActivity(activityId, userId, dtPostPone);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("StartActivity", ex, activityId, userId);
			}
		}
		/// <summary>
		/// Completes the activity.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <param name="xnData">The xn data.</param>
		/// <returns></returns>
		public bool CompleteActivity(int activityId, int userId)
		{
			try {
				ActivityItem activity = Global.DataAccess.GetActivity(activityId, userId);

				if ( activity == null ) {
					throw Global.CreateAndLogHumanActivityFault(
						"CompleteActivity",
						"Unable to get human activity informations.", null,
						activityId, userId);
				}

				XmlNode xnData = Global.DataAccess.GetActivityOutputData(activityId);

				EndpointAddress replyTo = new EndpointAddress(activity.ReplyUri);

				ChannelFactory<IHumanActivityServiceCallback> factory =
					new ChannelFactory<IHumanActivityServiceCallback>(
						new NetTcpBinding(), replyTo);
				IHumanActivityServiceCallback channel = factory.CreateChannel();

				using ( new OperationContextScope((IContextChannel)channel) ) {
					OperationContext.Current.OutgoingMessageHeaders.RelatesTo =
						new UniqueId(activity.MessageId);

					channel.HumanActivityCompleted(xnData);
				}
				Global.DataAccess.CompleteActivity(activityId, userId);
				return true;
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("CompleteActivity", ex, activityId, userId);
			}
		}
		/// <summary>
		/// Aborts the activity.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <param name="xnData">The xml data.</param>
		/// <returns></returns>
		public bool AbortActivity(int activityId, int userId, string message, string details)
		{
			try {
				ActivityItem activity = Global.DataAccess.GetActivity(activityId, userId);

				if ( activity == null ) {
					throw Global.CreateAndLogHumanActivityFault(
						"CompleteActivity",
						"Unable to get human activity instance informations.", null,
						activityId, userId);
				}
				EndpointAddress replyTo = new EndpointAddress(activity.ReplyUri);

				ChannelFactory<IHumanActivityServiceCallback> factory =
					new ChannelFactory<IHumanActivityServiceCallback>(
						new NetTcpBinding(), replyTo);
				IHumanActivityServiceCallback channel = factory.CreateChannel();

				using ( new OperationContextScope((IContextChannel)channel) ) {
					OperationContext.Current.OutgoingMessageHeaders.RelatesTo =
						new UniqueId(activity.MessageId);

					channel.HumanActivityAborted(new HumanActivityFault(message, activityId, userId, details));
				}
				Global.DataAccess.AbortActivity(activityId, userId, message, details);
				return true;
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("AbortActivity", ex, activityId, userId);
			}
		}
		/// <summary>
		/// Faults the activity.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <param name="xnData">The xml data.</param>
		/// <returns></returns>
		public bool FaultActivity(int activityId, int userId, string message, string details)
		{
			try {
				ActivityItem activity = Global.DataAccess.GetActivity(activityId, userId);

				if ( activity == null ) {
					throw Global.CreateAndLogHumanActivityFault(
						"FaultActivity",
						"Unable to get human activity informations.", null,
						activityId, userId);
				}
				EndpointAddress replyTo = new EndpointAddress(activity.ReplyUri);

				ChannelFactory<IHumanActivityServiceCallback> factory =
					new ChannelFactory<IHumanActivityServiceCallback>(
						new NetTcpBinding(), replyTo);
				IHumanActivityServiceCallback channel = factory.CreateChannel();
				using ( OperationContextScope scope = new OperationContextScope((IContextChannel)channel) ) {
					OperationContext.Current.OutgoingMessageHeaders.RelatesTo =
						new UniqueId(activity.MessageId);

					channel.HumanActivityFaulted(new HumanActivityFault(message, activityId, userId, details));
				}
				return Global.DataAccess.FaultActivity(activityId, userId, message, details);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("FaultActivity", ex, activityId, userId);
			}
		}
		/// <summary>
		/// Stops the activity.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <param name="message">The message.</param>
		/// <param name="details">The details.</param>
		/// <returns></returns>
		public bool StopActivity(int activityId, int userId, string message, string details)
		{
			return Global.DataAccess.StopActivity(activityId, userId, message, details);
		}

		/// <summary>
		/// Loads the data.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <returns></returns>
		public XmlNode LoadData(int activityId, int userId)
		{
			XmlNode xn = null;
			try {
				xn = Global.DataAccess.GetCurrentActivityData(activityId, userId);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("LoadData", ex, activityId, userId);
			}
			if ( xn == null ) {
				throw Global.CreateAndLogHumanActivityFault(
					"LoadData", "No input data present.", null, activityId, userId);
			} else {
				return xn;
			}
		}
		/// <summary>
		/// Saves the data.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <param name="userId">The user id.</param>
		/// <param name="xnData">The xn data.</param>
		/// <returns></returns>
		public bool SaveData(int activityId, int userId, XmlNode xnData)
		{
			if ( activityId <= 0 ) {
				throw Global.CreateAndLogHumanActivityFault(
					"SaveData", "Invalid activity id.", null, activityId, userId);
			}
			if ( xnData == null ) {
				throw Global.CreateAndLogHumanActivityFault(
					"SaveData", "No data available.", null, activityId, userId);
			}

			bool ok = false;
			try {
				ok = Global.DataAccess.SetActivityFormData(activityId, userId, xnData);
				if ( ok ) ok = Global.DataAccess.SetActivityOutputData(activityId, userId, xnData);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("SaveData", ex, activityId, userId);
			}
			return ok;
		}

		/// <summary>
		/// Sets the note.
		/// </summary>
		/// <param name="note">The note.</param>
		/// <returns></returns>
		public bool SetNote(NoteItem note)
		{
			try {
				int noteId;
				if ( note.NoteId.HasValue ) {
					noteId = note.NoteId.Value;
					return Global.DataAccess.UpdateNote(noteId, note.Subject, note.Text, note.LastModifiedById ?? -1);
				} else {
					return Global.DataAccess.AddNote(note.WorkflowInstanceId, note.ActivityId, note.Subject,
						note.Text, note.CreatedById, out noteId);
				}
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("SetNote", ex, note.ActivityId ?? -1, note.CreatedById);
			}
		}
		/// <summary>
		/// Deletes the note.
		/// </summary>
		/// <param name="noteId">The note id.</param>
		/// <returns></returns>
		public bool DeleteNote(int noteId)
		{
			try {
				return Global.DataAccess.DeleteNote(noteId);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("DeleteNote", ex);
			}
		}
		/// <summary>
		/// Gets the note items.
		/// </summary>
		/// <param name="activityId">The activity id.</param>
		/// <returns></returns>
		public List<NoteItem> GetNotes(int activityId)
		{
			try {
				return Global.DataAccess.GetNotesByActivity(activityId);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("GetNoteItems", ex, activityId, -1);
			}
		}

		/// <summary>
		/// Sets the attachment.
		/// </summary>
		/// <param name="attachment">The attachment.</param>
		/// <returns></returns>
		public bool SetAttachment(AttachmentItem attachment)
		{
			try {
				int attachmentId;
				if ( attachment.AttachmentId.HasValue ) {
					attachmentId = attachment.AttachmentId.Value;
					return Global.DataAccess.UpdateAttachment(
						attachmentId, attachment.Name,
						attachment.Description, attachment.FileName, attachment.Data,
						attachment.LastModifiedById);
				} else {
					return Global.DataAccess.AddAttachment(attachment.WorkflowInstanceId,
						attachment.ActivityId ?? -1, attachment.Name, attachment.Description,
						attachment.FileName, attachment.Data, attachment.CreatedById,
						out attachmentId);
				}
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("SetAttachment", ex);
			}
		}
		/// <summary>
		/// Deletes the attachment.
		/// </summary>
		/// <param name="attachmentId">The attachment id.</param>
		/// <returns></returns>
		public bool DeleteAttachment(int attachmentId)
		{
			try {
				return Global.DataAccess.DeleteAttachment(attachmentId, -1);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("DeleteAttachment", ex);
			}
		}
		/// <summary>
		/// Gets the attachment list.
		/// </summary>
		/// <param name="workflowInstanceId">The workflow instance id.</param>
		/// <returns></returns>
		public List<AttachmentItem> GetAttachmentList(Guid workflowInstanceId)
		{
			try {
				return Global.DataAccess.GetAttachmentsList(workflowInstanceId);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("GetAttachmentList", ex);
			}
		}
		/// <summary>
		/// Gets the attachment data.
		/// </summary>
		/// <param name="attachmentId">The attachment id.</param>
		/// <returns></returns>
		public byte[] GetAttachmentData(int attachmentId)
		{
			try {
				return Global.DataAccess.GetAttachmentData(attachmentId);
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("GetAttachmentData", ex);
			}
		}

		/// <summary>
		/// Gets the form.
		/// </summary>
		/// <returns></returns>
		public byte[] GetForm()
		{
			try {
				return (byte[])_row["hact_form_dll"];
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("GetForm", ex);
			}
		}
		/// <summary>
		/// Gets the form info.
		/// </summary>
		/// <returns></returns>
		public Contracts.FormInfo GetFormInfo()
		{
			FormInfo fi = new FormInfo();
			try {
				byte[] formDLL = (byte[])_row["hact_form_dll"];
				if ( formDLL != null && formDLL.Length > 0 ) {
					Assembly asm = Assembly.Load(formDLL);
					fi.Version = asm.GetName().Version.ToString();
				} else {
					fi.Version = "undefined";
				}
				fi.URL = "GetForm";
				fi.AssemblyName = _row["hact_form_asm_nm"] as string;
				fi.FormName = _row["hact_form_nm"] as string;
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("GetFormInfo", ex);
			}
			return fi;
		}

		public DataSet GetDelegations(int activityId)
		{
			throw Global.CreateAndLogHumanActivityFault("GetDelegations", new NotImplementedException());
		}
		public DataSet GetPossibleOrgs(int activityId)
		{
			throw Global.CreateAndLogHumanActivityFault("GetPossibleOrgs", new NotImplementedException());
		}

		public DataSet GetPossibleRoles(int activityId)
		{
			throw Global.CreateAndLogHumanActivityFault("GetPossibleRoles", new NotImplementedException());
		}

		public DataSet GetUserRoles(int activityId)
		{
			throw Global.CreateAndLogHumanActivityFault("GetUserRoles", new NotImplementedException());
		}

		public DataSet GetUsersForRole(int orgId, int roleId)
		{
			throw Global.CreateAndLogHumanActivityFault("GetUsersForRole", new NotImplementedException());
		}

		public bool UpdateActivityDueDate(int activityId, int userId, DateTime newDueDate, int remindeSecondsBefore)
		{
			throw Global.CreateAndLogHumanActivityFault("UpdateActivityDueDate", new NotImplementedException());
		}

		#endregion
	}
}
